package com.eden.api.security.interceptor;


import com.eden.api.security.annotation.AnonymousAccess;
import com.eden.api.security.annotation.PermissionsRequired;
import com.eden.api.security.annotation.RolesRequired;
import com.eden.api.security.enums.RequiredMode;
import com.eden.api.security.service.AuthorizationService;
import com.eden.common.constant.HttpStatus;
import com.eden.common.exception.api.auth.UnauthorizedException;
import com.eden.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * @description: 授权拦截器
 * @author: wenqing
 * @date: 2019/11/04 22:59
 */

@Slf4j
@Component
public class AuthorizationInterceptor extends BaseAuthInterceptor {
    @Autowired
    private AuthorizationService authorizationService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;

        if (hasAnonymousAccess(handlerMethod)) {
            return true;
        }

        boolean hasPermissions = checkPermissionsRequired(handlerMethod);
        boolean hasRoles = checkRolesRequired(handlerMethod);
        if (!hasPermissions) {
            reject(request, response, requiredPermissions(handlerMethod), 0);
            return false;
        } else if (!hasRoles) {
            reject(request, response, requiredRoles(handlerMethod), 1);
            return false;
        } else {
            return true;
        }
    }

    /**
     * 判断是否匿名访问
     * @param handlerMethod
     * @return
     */
    private boolean hasAnonymousAccess(HandlerMethod handlerMethod) {
        boolean hasAnonymousAccess = false;
        Class clazz = handlerMethod.getBeanType();
        AnonymousAccess annotation;
        if(clazz.isAnnotationPresent(AnonymousAccess.class)) {
            annotation = (AnonymousAccess) clazz.getAnnotation(AnonymousAccess.class);
            if(log.isDebugEnabled()){
                String clazzName = clazz.getSimpleName();
                String annotationName = annotation.annotationType().getSimpleName();
                log.debug("在类{}上找到注解{}", clazzName, annotationName);
            }
            hasAnonymousAccess = true;
        }
        Method method = handlerMethod.getMethod();
        if (method.isAnnotationPresent(AnonymousAccess.class)) {
            annotation = method.getAnnotation(AnonymousAccess.class);
            if(log.isDebugEnabled()){
                String clazzName = handlerMethod.getBeanType().getName();
                String methodName = handlerMethod.getMethod().getName();
                String annotationName = annotation.annotationType().getName();
                log.debug("在类{}的{}方法上找到注解{}", clazzName, methodName, annotationName);
            }
            hasAnonymousAccess = true;
        }
        return hasAnonymousAccess;
    }

    /** 
     * @description: 判断用户是否拥有某些权限
     * @date: 2019/11/17 18:51
     * @author: wenqing
     * @param handlerMethod
     * @return: boolean
     * @throws: 
     */ 
    private boolean checkPermissionsRequired(HandlerMethod handlerMethod) {
        boolean hasPermissions = true;
        Method method = handlerMethod.getMethod();
        PermissionsRequired annotation;
        if (method.isAnnotationPresent(PermissionsRequired.class)) {
            annotation = method.getAnnotation(PermissionsRequired.class);
            if(log.isDebugEnabled()){
                String clazzName = handlerMethod.getBeanType().getName();
                String methodName = handlerMethod.getMethod().getName();
                String annotationName = annotation.annotationType().getName();
                String[] annotationValue = annotation.value();
                RequiredMode requiredMode = annotation.requiredMode();
                log.debug("在类{}的{}方法上找到注解{}，value={}，requiredMode={}", clazzName, methodName, annotationName, annotationValue, requiredMode);
            }
            hasPermissions = authorizationService.hasPermissions(annotation);
        }
        return hasPermissions;
    }

    private Set<String> requiredPermissions(HandlerMethod handlerMethod) {
        Method method = handlerMethod.getMethod();
        PermissionsRequired annotation;
        if (method.isAnnotationPresent(PermissionsRequired.class)) {
            annotation = method.getAnnotation(PermissionsRequired.class);
            String[] annotationValue = annotation.value();
            Set<String> requiresPermissions = new HashSet<>(Arrays.asList(annotationValue));
            return requiresPermissions;
        } else {
            return new HashSet<>(0);
        }
    }

    /**
     * @description: 判断用户是否拥有某些角色
     * @date: 2019/11/17 18:51
     * @author: wenqing
     * @param handlerMethod
     * @return: boolean
     * @throws:
     */
    private boolean checkRolesRequired(HandlerMethod handlerMethod) {
        boolean hasRoles = true;
        Method method = handlerMethod.getMethod();
        RolesRequired annotation;
        if (method.isAnnotationPresent(RolesRequired.class)) {
            annotation = method.getAnnotation(RolesRequired.class);
            if(log.isDebugEnabled()){
                String clazzName = handlerMethod.getBeanType().getName();
                String methodName = handlerMethod.getMethod().getName();
                String annotationName = annotation.annotationType().getName();
                String[] annotationValue = annotation.value();
                RequiredMode requiredMode = annotation.requiredMode();
                log.debug("在类{}的{}方法上找到注解{}，value={}，requiredMode={}", clazzName, methodName, annotationName, annotationValue, requiredMode);
            }
            hasRoles = authorizationService.hasRoles(annotation);
        }
        return hasRoles;
    }

    private Set<String> requiredRoles(HandlerMethod handlerMethod) {
        Method method = handlerMethod.getMethod();
        RolesRequired annotation;
        if (method.isAnnotationPresent(RolesRequired.class)) {
            annotation = method.getAnnotation(RolesRequired.class);
            String[] annotationValue = annotation.value();
            Set<String> requiredRoles = new HashSet<>(Arrays.asList(annotationValue));
            return requiredRoles;
        } else {
            return new HashSet<>(0);
        }
    }

    private void reject(HttpServletRequest request, HttpServletResponse response, Set<String> info, Integer type) {
        String msg = StringUtils.format("您没有执行：{}操作的权限，请联系管理员授权", request.getRequestURI());
        if (StringUtils.isNotEmpty(info)) {
            switch (type) {
                case 0:
                    msg = msg + info;
                    break;
                case 1:
                    msg = msg +"角色"+ info;
                    break;
                default:
            }
        }
        //ServletUtils.renderString(response, JSON.toJSONString(ApiResult.error(HttpStatus.UNAUTHORIZED, msg)));
        throw new UnauthorizedException(HttpStatus.FORBIDDEN, msg);
    }
}
